#include "cpu.h"
#include "printk.h"

inline void get_cpuid(unsigned int m_op, unsigned int s_op, unsigned int *a, unsigned int *b, unsigned int *c, unsigned int *d) {
    __asm__ __volatile__ (
        "cpuid \n\t"
        :"=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
        :"0"(m_op), "2"(s_op)
    );
}

void cpu_init(void) {
    int i, j;
    unsigned int cpu_fac_name[4] = {0, 0, 0, 0};
    char factor_name[17] = {0};

    // vender_string
    get_cpuid(0, 0, &cpu_fac_name[0], &cpu_fac_name[1], &cpu_fac_name[2], &cpu_fac_name[3]);
    *(unsigned int *)&factor_name[0] = cpu_fac_name[1];
    *(unsigned int *)&factor_name[4] = cpu_fac_name[3];
    *(unsigned int *)&factor_name[8] = cpu_fac_name[2];
    factor_name[12] = '\0';
    color_printk(YELLOW, BLACK, "%s\t%#010x\t%#010x\t%#010x\n", factor_name, cpu_fac_name[1], cpu_fac_name[3], cpu_fac_name[2]);

    // brand string
    for (i = 0x80000002; i < 0x80000005; ++i) {
        get_cpuid(i, 0, &cpu_fac_name[0], &cpu_fac_name[1], &cpu_fac_name[2], &cpu_fac_name[3]);
        *(unsigned int *)&factor_name[0] = cpu_fac_name[0];
        *(unsigned int *)&factor_name[4] = cpu_fac_name[1];
        *(unsigned int *)&factor_name[8] = cpu_fac_name[2];
        *(unsigned int *)&factor_name[12] = cpu_fac_name[3];
        factor_name[16] = '\0';
        color_printk(YELLOW, BLACK, "%s", factor_name);
    }
    color_printk(YELLOW, BLACK, "\n");

    // version information type, family, model, and stepping ID
    get_cpuid(1, 0, &cpu_fac_name[0], &cpu_fac_name[1], &cpu_fac_name[2], &cpu_fac_name[3]);
    color_printk(YELLOW, BLACK, "Family Code:%#010x, Extended Family:%#010x, Model Number:%#010x, Extended Model:%#010x, Processor Type:%#010x, Stepping ID:%#010x\n", 
            (cpu_fac_name[0] >> 8 & 0xf), (cpu_fac_name[0] >> 20 & 0xff), (cpu_fac_name[0] >> 4 & 0xf), (cpu_fac_name[0] >> 16 & 0xf), (cpu_fac_name[0] >> 12 & 0x3), (cpu_fac_name[0] & 0xf));
    
    // get Linear/Physical Address size
    get_cpuid(0x80000008, 0, &cpu_fac_name[0], &cpu_fac_name[1], &cpu_fac_name[2], &cpu_fac_name[3]);
    color_printk(YELLOW, BLACK, "Physical Address size:%08d, Linear Address size:%08d\n", (cpu_fac_name[0] & 0xff), (cpu_fac_name[0] >> 8 & 0xff));

    // max cpuid operation code
    get_cpuid(0, 0, &cpu_fac_name[0], &cpu_fac_name[1], &cpu_fac_name[2], &cpu_fac_name[3]);
    color_printk(WHITE, BLACK, "MAX Basic Operation Code:%010x\t", cpu_fac_name[0]);
    get_cpuid(0x80000000, 0, &cpu_fac_name[0], &cpu_fac_name[1], &cpu_fac_name[2], &cpu_fac_name[3]);
    color_printk(WHITE, BLACK, "MAX Extended Operation Code:%010x\n", cpu_fac_name[0]);
}